/**
 * 
 */
package cs342.project1.mathNotebook.notebook;

import java.util.*;

import cs342.project1.mathNotebook.data.Parser;
import cs342.project1.mathNotebook.expressions.*;
import cs342.project1.mathNotebook.expressions.exceptions.UncapturedVariableException;

/**
 * Holds information about a notebook entry
 * 
 * @author Jared Schlicht
 */
public class NotebookEntry {
	private final double partitionSize = 0.00001;
	
    public double minXCoordinate;
    public double maxXCoordinate;
    public double minYCoordinate;
    public double maxYCoordinate;

    public Expr function;
    public String functionString;

    private final static double epsilon = 0.0000001;

    public List<Double> tangentPoints;
    
    public NotebookEntry() {
	this.tangentPoints = new ArrayList<Double>();
    }
    
    /**
     * Calculates the area under the given expression between the specified
     * x coordinates.
     * @return
     */
    public double calculateArea() {
    	double area = 0.0;
    	
    	double xCoordinate = minXCoordinate;
    	while (xCoordinate < maxXCoordinate) {
    		EvaluationVisitor visitor = new EvaluationVisitor("x", xCoordinate);
    		try {
				double yCoordinate =  (Double)function.accept(visitor);
				area += yCoordinate * partitionSize;
				xCoordinate += partitionSize;
			} catch (UncapturedVariableException e) {
			}
    	}
    	
    	return area;
    }
    
	/**
	 * Gets an expression that represents a tangent line to the given
	 * expression at the specified point
	 * 
	 * @author Alejandro
	 * @param exp finds a line tangent to this equation
	 * @param x the point at which the tangent line and specified equation intersect
	 * @return Expr object 
	 */
	public Expr getTangentLineAt(double x){
		Parser p = new Parser();
		String tangentLine = "";
		double slope = findSlopeAt(this.function,x);
		
		// TODO: build this with objects, don't rely on the parser
		tangentLine = ""+slope + "*(x-" + x + ")+" + evaluateExpression(this.function,x);
		
		Expr result =
		    new AddExpr(
			    new MultExpr(
				    new LiteralExpr(slope),
				    new SubExpr(
					    new VarExpr("x"),
					    new LiteralExpr(x)
				    )
			    ),
			    new LiteralExpr(evaluateExpression(this.function,x)));
		
		return result;
		// y = m(x-x1)+y1
		// Expr temp = p.getExpression(tangentLine);
		
		// evaluateExpression(temp,0);
		// return temp;
	}
	
	/**
	 * Finds the slope at the specified point
	 * @param exp
	 * @param x
	 * @author Alejandro
	 * @return
	 */
	public static double findSlopeAt(Expr exp,double x){
		double y = evaluateExpression(exp, x);
		double step = 0.005;
		
		double x2 = x;
		double y2 = 0;
		int cont = 0;
		
		double slopeO = 0;
		double slopeN = 0;
		
		while(step > -0.05){
			x2 = x2 + step;
			y2 = evaluateExpression(exp, x2);
			step -= 0.001;
			slopeN = (y - y2)/(x - x2);
			if(Math.abs(slopeO-slopeN)<epsilon && cont>0)	
				break;
			slopeO = slopeN;
			cont++;
		}
		//System.out.println(slopeN);
		return slopeN;
	}
    
	/**
	 * Evaluates the specified expression at the given point
	 * @param exp expression to evaluate
	 * @param x the point to evaluate at
	 * @author Alejandro
	 * @return the value of the expression
	 */
	private static double evaluateExpression(Expr exp,double x){
		EvaluationVisitor ev = new EvaluationVisitor("x", x);
		double value = 0;
		try{
		value = (Double) exp.accept(ev);
		}catch(Exception e){
			
		}
		return value;
	}
	
	public String toString(){
		return functionString;
	}
	
}

